package com.chensi.admin.shop.service.impl;

import com.chensi.admin.shop.common.BaseErrorCode;
import com.chensi.admin.shop.common.Constants;
import com.chensi.admin.shop.common.SortParam;
import com.chensi.admin.shop.domain.Goods;
import com.chensi.admin.shop.dto.GoodsDTO;
import com.chensi.admin.shop.dto.mapper.GoodsMapper;
import com.chensi.admin.shop.service.GoodsService;
import com.chensi.admin.shop.util.BeanUtils;
import com.chensi.admin.shop.util.CommonUtil;
import com.chensi.admin.shop.util.SpecificationUtil;
import com.chensi.admin.shop.dao.GoodsPicRepository;
import com.chensi.admin.shop.dao.GoodsRepository;
import com.chensi.admin.shop.exception.BaseException;
import lombok.AllArgsConstructor;
import org.apache.commons.lang.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author si.chen
 * @date 2019/7/9 9:31
 */
@Service
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
@AllArgsConstructor
public class GoodsServiceImpl implements GoodsService {

    private final GoodsRepository goodsRepository;

    private final GoodsPicRepository goodsPicRepository;

    private final GoodsMapper goodsMapper;

    @Override
    public Goods get(String id) throws BaseException {
        Optional<Goods> optional = goodsRepository.findById(id);
        return optional.orElseThrow(() -> new BaseException(BaseErrorCode.ITEM_NOT_FOUND));
    }

    @Override
    
    public GoodsDTO detail(String id) throws BaseException {
        return goodsMapper.toDTO(this.get(id));
    }

    @Override
    public GoodsDTO find(Goods goods) {
        Optional<Goods> optional = goodsRepository.findOne(this.getSpecification(goods));
        return goodsMapper.toDTO(optional.orElse(null));
    }

    @Override
    public List<GoodsDTO> list(Goods goods) {
        Sort sort = this.getSort(goods.getSortParam());
        return goodsRepository.findAll(this.getSpecification(goods), sort)
                .stream().map(goodsMapper::toDTO).collect(Collectors.toList());
    }

    @Override
    public Page<GoodsDTO> page(Goods goods) {
        //page模糊查询
        goods.setNameLike(goods.getName());
        goods.setName(null);
        Sort sort = this.getSort(goods.getSortParam());
        Pageable pageable = PageRequest.of(goods.getPageParam().getPageNo(), goods.getPageParam().getPageSize(), sort);
        return goodsRepository.findAll(this.getSpecification(goods), pageable).map(goodsMapper::toDTO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(Goods goods) throws BaseException {
        this.checkUniqueForCreate(goods);
        //计算最大金额最小金额
        if (goods.getPriceMax() == null) {
            goods.setPriceMax(goods.getPrice());
        }
        if (goods.getPriceMin() == null) {
            goods.setPriceMin(goods.getPrice());
        }
        goods.setIsRecommended(Constants.NO);
        //保存图片
        goods.getGoodsPicList().forEach(pic -> pic.setGoods(goods));
        goodsRepository.save(goods);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(Goods goods) throws BaseException {
        Goods entity = this.get(goods.getId());
        this.checkUniqueForUpdate(entity);
        //先删除子对象
        entity.getGoodsPicList().forEach(goodsPicRepository::delete);
        BeanUtils.copyNotNullProperties(goods, entity);
        //再新增子对象
        entity.getGoodsPicList().forEach(pic -> pic.setGoods(entity));
        goodsRepository.save(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(String id) throws BaseException {
        Goods entity = this.get(id);
        //先删除子对象
        entity.getGoodsPicList().forEach(goodsPicRepository::delete);
        entity.setDeleted(Constants.YES);
        goodsRepository.save(entity);
    }

    @SuppressWarnings("Duplicates")
    private void checkUniqueForCreate(Goods entity) throws BaseException {
        if (StringUtils.isNotBlank(entity.getName())) {
            Goods param = new Goods();
            param.setName(entity.getName().trim());
            if (CommonUtil.isNotEmptyList(this.list(param))) {
                throw new BaseException(BaseErrorCode.NAME_IS_USED);
            }
        }
    }

    @SuppressWarnings("Duplicates")
    private void checkUniqueForUpdate(Goods entity) throws BaseException {
        if (StringUtils.isNotBlank(entity.getName())) {
            Goods param = new Goods();
            param.setName(entity.getName().trim());
            param.setIdNe(entity.getId().trim());
            if (CommonUtil.isNotEmptyList(this.list(param))) {
                throw new BaseException(BaseErrorCode.NAME_IS_USED);
            }
        }
    }

    private Sort getSort(SortParam sortParam) {
        if (sortParam != null) {
            return new Sort(sortParam.getDirection(), sortParam.getField());
        } else {
            return new Sort(Sort.Direction.ASC, "createTime");
        }
    }

    @SuppressWarnings("Duplicates")
    private Specification<Goods> getSpecification(Goods entity) {
        return (root, query, cb) -> {
            List<Predicate> list = new ArrayList<>();
            SpecificationUtil.baseSpecification(root, cb, list, entity);
            if (StringUtils.isNotBlank(entity.getName())) {
                Predicate predicate = cb.equal(root.get("name"), entity.getName().trim());
                list.add(predicate);
            }
            if (entity.getCompany() != null && StringUtils.isNotBlank(entity.getCompany().getId())) {
                Predicate predicate = cb.equal(root.get("company").get("id"), entity.getCompany().getId().trim());
                list.add(predicate);
            }
            if (entity.getGoodsType() != null && StringUtils.isNotBlank(entity.getGoodsType().getId())) {
                Predicate predicate = cb.equal(root.get("goodsType").get("id"), entity.getGoodsType().getId().trim());
                list.add(predicate);
            }
            if (entity.getGoodsUnit() != null && StringUtils.isNotBlank(entity.getGoodsUnit().getId())) {
                Predicate predicate = cb.equal(root.get("goodsUnit").get("id"), entity.getGoodsUnit().getId().trim());
                list.add(predicate);
            }
            if (entity.getIsRecommended() != null) {
                Predicate predicate = cb.equal(root.get("isRecommended"), entity.getIsRecommended());
                list.add(predicate);
            }
            if (entity.getPriceStart() != null && entity.getPriceEnd() != null) {
                Predicate lt = cb.lessThanOrEqualTo(root.get("price"), entity.getPriceEnd());
                Predicate gt = cb.greaterThanOrEqualTo(root.get("price"), entity.getPriceStart());
                list.add(lt);
                list.add(gt);
            }
            query.where(list.toArray(new Predicate[0]));
            return query.getRestriction();
        };
    }
}
